package cn.kimming.rpc.remoting.server;

import cn.kimming.rpc.common.annotation.RpcService;
import cn.kimming.rpc.common.constant.RpcConstant;
import cn.kimming.rpc.common.exception.RpcException;
import cn.kimming.rpc.remoting.registry.InstanceDetail;
import cn.kimming.rpc.remoting.registry.ServerRpcRegistry;
import org.apache.curator.x.discovery.ServiceDiscovery;
import org.apache.curator.x.discovery.ServiceInstance;
import org.apache.curator.x.discovery.ServiceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;

import java.net.InetAddress;
import java.util.Collection;
import java.util.Map;
import java.util.UUID;

public class SpringBootRpcServer extends AbstractRpcServer{

    private static final Logger logger = LoggerFactory.getLogger(SpringBootRpcServer.class);
    private ApplicationContext applicationContext;

    public SpringBootRpcServer(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public SpringBootRpcServer(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void start() {
        logger.info("[my-rpc] begin to start rpc server");
        // 获取所有服务实现
        logger.info("[my-rpc] begin to initialize serviceMap");
        scanAllRpcService();
        // 将服务注册到注册中心上
        logger.info("[my-rpc] begin to register service to zk");
        registerServices();
        // 启动网络服务器, 接收请求
        logger.info("[my-rpc] begin to start network server");
        startNetworkServer();
    }

    @Override
    protected void shutdown() {

    }

    @Override
    protected void scanAllRpcService() {
        Map<String, Object> beans = applicationContext.getBeansWithAnnotation(RpcService.class);
        Collection<Object> serviceImpls = beans.values();
        serviceMap = new ServiceMap(serviceImpls);
    }

    @Override
    protected void registerServices() {
        Environment environment = applicationContext.getEnvironment();
        registryUrl = environment.getProperty(RpcConstant.MY_RPC_ZK_URL, RpcConstant.MY_RPC_ZK_URL_DEFAULT_VALUE);
        port = environment.getProperty(RpcConstant.MY_RPC_SERVER_PORT, Integer.class, RpcConstant.MY_RPC_SERVER_PORT_DEFAULT_VALUE);

        // 初始化服务端zk连接
        ServerRpcRegistry.init(registryUrl);
        // 获取ServiceDiscovery
        ServiceDiscovery<InstanceDetail> discovery = ServerRpcRegistry.getDiscovery();

        for (Map.Entry<String, Object> entry : serviceMap.entrySet()) {
            String serviceName = entry.getKey();
            Object serviceImpl = entry.getValue();
            if (registerdServiceMap.containsKey(serviceName)) {
                throw new RpcException(serviceName + "存在多个服务实现, 请勿重复注册");
            }

            RpcService rpcService = serviceImpl.getClass().getAnnotation(RpcService.class);
            try {
                InstanceDetail instanceDetail = new InstanceDetail(rpcService.version(), rpcService.description());
                String serviceId = UUID.randomUUID().toString();
                ServiceInstance<InstanceDetail> instance = ServiceInstance.<InstanceDetail>builder()
                        .id(serviceId)
                        .serviceType(ServiceType.DYNAMIC)
                        .address(InetAddress.getLocalHost().getHostAddress())
                        .port(port)
                        .payload(instanceDetail)
                        .name(serviceName)
                        .build();
                discovery.registerService(instance);
                registerdServiceMap.put(serviceName, serviceId);

                logger.info("[my-rpc register serviceName: {}, impl: {} to service registry]", serviceName, serviceImpl);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    @Override
    protected void startNetworkServer() {
        new Thread() {
            @Override
            public void run() {
                networkServer = new NettyServer();
                networkServer.start(port, serviceMap);
            }
        }.start();

    }
}
